/* Copyright (C) 2006 - 2010 TrinityCore <https://www.trinitycore.org/>
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "ScriptedPch.h"
#include "halls_of_reflection.h"

enum Yells
{
    SAY_JAINA_INTRO_1                             = -1668001,
    SAY_JAINA_INTRO_2                             = -1668002,
    SAY_JAINA_INTRO_3                             = -1668003,
    SAY_JAINA_INTRO_4                             = -1668004,
    SAY_UTHER_INTRO_A2_1                          = -1668005,
    SAY_JAINA_INTRO_5                             = -1668006,
    SAY_UTHER_INTRO_A2_2                          = -1668007,
    SAY_JAINA_INTRO_6                             = -1668008,
    SAY_UTHER_INTRO_A2_3                          = -1668009,
    SAY_JAINA_INTRO_7                             = -1668010,
    SAY_UTHER_INTRO_A2_4                          = -1668011,
    SAY_JAINA_INTRO_8                             = -1668012,
    SAY_UTHER_INTRO_A2_5                          = -1668013,
    SAY_JAINA_INTRO_9                             = -1668014,
    SAY_UTHER_INTRO_A2_6                          = -1668015,
    SAY_UTHER_INTRO_A2_7                          = -1668016,
    SAY_JAINA_INTRO_10                            = -1668017,
    SAY_UTHER_INTRO_A2_8                          = -1668018,
    SAY_JAINA_INTRO_11                            = -1668019,
    SAY_UTHER_INTRO_A2_9                          = -1668020,

    SAY_SYLVANAS_INTRO_1                          = -1668021,
    SAY_SYLVANAS_INTRO_2                          = -1668022,
    SAY_SYLVANAS_INTRO_3                          = -1668023,
    SAY_UTHER_INTRO_H2_1                          = -1668024,
    SAY_SYLVANAS_INTRO_4                          = -1668025,
    SAY_UTHER_INTRO_H2_2                          = -1668026,
    SAY_SYLVANAS_INTRO_5                          = -1668027,
    SAY_UTHER_INTRO_H2_3                          = -1668028,
    SAY_SYLVANAS_INTRO_6                          = -1668029,
    SAY_UTHER_INTRO_H2_4                          = -1668030,
    SAY_SYLVANAS_INTRO_7                          = -1668031,
    SAY_UTHER_INTRO_H2_5                          = -1668032,
    SAY_UTHER_INTRO_H2_6                          = -1668033,
    SAY_SYLVANAS_INTRO_8                          = -1668034,
    SAY_UTHER_INTRO_H2_7                          = -1668035,

    SAY_LK_INTRO_1                                = -1668036,
    SAY_LK_INTRO_2                                = -1668037,
    SAY_LK_INTRO_3                                = -1668038,
    SAY_FALRIC_INTRO_1                            = -1668039,
    SAY_MARWYN_INTRO_1                            = -1668040,
    SAY_FALRIC_INTRO_2                            = -1668041,

    SAY_JAINA_INTRO_END                           = -1668042,
    SAY_SYLVANAS_INTRO_END                        = -1668043,
};

enum Events
{
    EVENT_NONE,
    
    EVENT_START_INTRO,
    EVENT_SKIP_INTRO,

    EVENT_INTRO_A2_1,
    EVENT_INTRO_A2_2,
    EVENT_INTRO_A2_3,
    EVENT_INTRO_A2_4,
    EVENT_INTRO_A2_5,
    EVENT_INTRO_A2_6,
    EVENT_INTRO_A2_7,
    EVENT_INTRO_A2_8,
    EVENT_INTRO_A2_9,
    EVENT_INTRO_A2_10,
    EVENT_INTRO_A2_11,
    EVENT_INTRO_A2_12,
    EVENT_INTRO_A2_13,
    EVENT_INTRO_A2_14,
    EVENT_INTRO_A2_15,
    EVENT_INTRO_A2_16,
    EVENT_INTRO_A2_17,
    EVENT_INTRO_A2_18,
    EVENT_INTRO_A2_19,

    EVENT_INTRO_H2_1,
    EVENT_INTRO_H2_2,
    EVENT_INTRO_H2_3,
    EVENT_INTRO_H2_4,
    EVENT_INTRO_H2_5,
    EVENT_INTRO_H2_6,
    EVENT_INTRO_H2_7,
    EVENT_INTRO_H2_8,
    EVENT_INTRO_H2_9,
    EVENT_INTRO_H2_10,
    EVENT_INTRO_H2_11,
    EVENT_INTRO_H2_12,
    EVENT_INTRO_H2_13,
    EVENT_INTRO_H2_14,
    EVENT_INTRO_H2_15,

    EVENT_INTRO_LK_1,
    EVENT_INTRO_LK_2,
    EVENT_INTRO_LK_3,
    EVENT_INTRO_LK_4,
    EVENT_INTRO_LK_5,
    EVENT_INTRO_LK_6,
    EVENT_INTRO_LK_7,
    EVENT_INTRO_LK_8,
    EVENT_INTRO_LK_9,

    EVENT_INTRO_END,
};

enum eEnum
{
    ACTION_START_INTRO,
    ACTION_SKIP_INTRO,

    QUEST_DELIVRANCE_FROM_THE_PIT_A2              = 24710,
    QUEST_DELIVRANCE_FROM_THE_PIT_H2              = 24712,
    QUEST_WRATH_OF_THE_LICH_KING_A2               = 24500,
    QUEST_WRATH_OF_THE_LICH_KING_H2               = 24802,
}; 

static Position HallsofReflectionLocs[]=
{
    {5283.234863, 1990.946777, 707.695679, 0.929097},   // 2 Loralen Follows
    {5408.031250, 2102.918213, 707.695251, 0.792756},   // 9 Sylvanas Follows
    {5401.866699, 2110.837402, 707.695251, 0.800610},   // 10 Loralen follows
};

static Position SpawnPos                = {5262.540527, 1949.693726, 707.695007, 0.808736}; // Jaina/Sylvanas Beginning Position
static Position MoveThronePos           = {5306.952148, 1998.499023, 709.341431, 1.277278}; // Jaina/Sylvanas walks to throne
static Position UtherSpawnPos           = {5308.310059, 2003.857178, 709.341431, 4.650315};
static Position LichKingSpawnPos        = {5362.917480, 2062.307129, 707.695374, 3.945812};
static Position LichKingMoveThronePos   = {5312.080566, 2009.172119, 709.341431, 3.973301}; // Lich King walks to throne
static Position LichKingMoveAwayPos     = {5400.069824, 2102.7131689, 707.69525, 0.843803}; // Lich King walks away

// AI of Part1: handle the intro till start of gauntlet event.
struct npc_jaina_or_sylvanas_horAI : public ScriptedAI
{
    npc_jaina_or_sylvanas_horAI(Creature *pCreature) : ScriptedAI(pCreature) 
    {
        pInstance = me->GetInstanceData();
    }

    ScriptedInstance* pInstance;
    uint64 uiUther;
    uint64 uiLichKing;

    EventMap events;

    void Reset()
    {
        events.Reset();

        uiUther = 0;
        uiLichKing = 0;

        me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
        me->SetStandState(UNIT_STAND_STATE_STAND);
        me->SetVisibility(VISIBILITY_ON);
    }

    void DoAction(const int32 actionId) 
    {
        switch(actionId)
        {
            case ACTION_START_INTRO:
                events.ScheduleEvent(EVENT_START_INTRO, 0);
                break;
            case ACTION_SKIP_INTRO:
                events.ScheduleEvent(EVENT_SKIP_INTRO, 0);
                break;
        }
    }

    void UpdateAI(const uint32 diff)
    {
        events.Update(diff);
        switch(events.ExecuteEvent())
        {
            case EVENT_START_INTRO:
                me->GetMotionMaster()->MovePoint(0, MoveThronePos);
                // Begining of intro is differents between factions as the speech sequence and timers are differents.
                if (pInstance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE)
                    events.ScheduleEvent(EVENT_INTRO_A2_1, 0);
                else
                    events.ScheduleEvent(EVENT_INTRO_H2_1, 0);
                break;

        // A2 Intro Events
            case EVENT_INTRO_A2_1:
                DoScriptText(SAY_JAINA_INTRO_3, me);
                events.ScheduleEvent(EVENT_INTRO_A2_2, 5000);
                break;
            case EVENT_INTRO_A2_2:
                DoScriptText(SAY_JAINA_INTRO_4, me);
                events.ScheduleEvent(EVENT_INTRO_A2_3, 10000);
                break;
            case EVENT_INTRO_A2_3:
                // TODO: she's doing some kind of spell casting emote
                pInstance->HandleGameObject(pInstance->GetData64(DATA_FROSTMOURNE), true);
                events.ScheduleEvent(EVENT_INTRO_A2_4, 10000);
                break;
            case EVENT_INTRO_A2_4:
                // spawn UTHER during speach 2
                if (Creature* pUther = me->SummonCreature(NPC_UTHER, UtherSpawnPos, TEMPSUMMON_MANUAL_DESPAWN))
                {
                    pUther->GetMotionMaster()->MoveIdle();
                    pUther->SetReactState(REACT_PASSIVE); // be sure he will not aggro arthas
                    uiUther = pUther->GetGUID();
                }
                events.ScheduleEvent(EVENT_INTRO_A2_5, 2000);
                break;
            case EVENT_INTRO_A2_5:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_1, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_6, 3000);
                break;
            case EVENT_INTRO_A2_6:
                DoScriptText(SAY_JAINA_INTRO_5, me);
                events.ScheduleEvent(EVENT_INTRO_A2_7, 6000);
                break;
            case EVENT_INTRO_A2_7:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_2, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_8, 6500);
                break;
            case EVENT_INTRO_A2_8:
                DoScriptText(SAY_JAINA_INTRO_6, me);
                events.ScheduleEvent(EVENT_INTRO_A2_9, 2000);
                break;
            case EVENT_INTRO_A2_9:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_3, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_10, 9000);
                break;
            case EVENT_INTRO_A2_10:
                DoScriptText(SAY_JAINA_INTRO_7, me);
                events.ScheduleEvent(EVENT_INTRO_A2_11, 5000);
                break;
            case EVENT_INTRO_A2_11:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_4, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_12, 11000);
                break;
            case EVENT_INTRO_A2_12:
                DoScriptText(SAY_JAINA_INTRO_8, me);
                events.ScheduleEvent(EVENT_INTRO_A2_13, 4000);
                break;
            case EVENT_INTRO_A2_13:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_5, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_14, 12500);
                break;
            case EVENT_INTRO_A2_14:
                DoScriptText(SAY_JAINA_INTRO_9, me);
                events.ScheduleEvent(EVENT_INTRO_A2_15, 10000);
                break;
            case EVENT_INTRO_A2_15:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_6, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_16, 22000);
                break;
            case EVENT_INTRO_A2_16:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_A2_7, pUther);
                events.ScheduleEvent(EVENT_INTRO_A2_17, 4000);
                break;
            case EVENT_INTRO_A2_17:
                DoScriptText(SAY_JAINA_INTRO_10, me);
                events.ScheduleEvent(EVENT_INTRO_A2_18, 2000);
                break;
            case EVENT_INTRO_A2_18:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                {
                    pUther->HandleEmoteCommand(EMOTE_ONESHOT_NO);
                    DoScriptText(SAY_UTHER_INTRO_A2_8, pUther);
                }
                events.ScheduleEvent(EVENT_INTRO_A2_19, 11000);
                break;
            case EVENT_INTRO_A2_19:
                DoScriptText(SAY_JAINA_INTRO_11, me);
                events.ScheduleEvent(EVENT_INTRO_LK_1, 2000);
                break;

        // H2 Intro Events
            case EVENT_INTRO_H2_1:
                DoScriptText(SAY_SYLVANAS_INTRO_1, me);
                events.ScheduleEvent(EVENT_INTRO_H2_2, 8000);
                break;
            case EVENT_INTRO_H2_2:
                DoScriptText(SAY_SYLVANAS_INTRO_2, me);
                events.ScheduleEvent(EVENT_INTRO_H2_3, 6000);
                break;
            case EVENT_INTRO_H2_3:
                DoScriptText(SAY_SYLVANAS_INTRO_3, me);
                // TODO: she's doing some kind of spell casting emote
                events.ScheduleEvent(EVENT_INTRO_H2_4, 6000);
                break;
            case EVENT_INTRO_H2_4:
                // spawn UTHER during speach 2
                if (Creature* pUther = me->SummonCreature(NPC_UTHER, UtherSpawnPos, TEMPSUMMON_MANUAL_DESPAWN))
                {
                    pUther->GetMotionMaster()->MoveIdle();
                    pUther->SetReactState(REACT_PASSIVE); // be sure he will not aggro arthas
                    uiUther = pUther->GetGUID();
                }
                events.ScheduleEvent(EVENT_INTRO_H2_5, 2000);
                break;
            case EVENT_INTRO_H2_5:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_H2_1, pUther);
                events.ScheduleEvent(EVENT_INTRO_H2_6, 11000);
                break;
            case EVENT_INTRO_H2_6:
                DoScriptText(SAY_SYLVANAS_INTRO_4, me);
                events.ScheduleEvent(EVENT_INTRO_H2_7, 3000);
                break;
            case EVENT_INTRO_H2_7:
                if (Creature* pUther = me->GetCreature(*me, uiUther)) 
                    DoScriptText(SAY_UTHER_INTRO_H2_2, pUther);
                events.ScheduleEvent(EVENT_INTRO_H2_8, 6000);
                break;
            case EVENT_INTRO_H2_8:
                DoScriptText(SAY_SYLVANAS_INTRO_5, me);
                events.ScheduleEvent(EVENT_INTRO_H2_9, 5000);
                break;
            case EVENT_INTRO_H2_9:
                if (Creature* pUther = me->GetCreature(*me, uiUther))
                    DoScriptText(SAY_UTHER_INTRO_H2_3, pUther);
                events.ScheduleEvent(EVENT_INTRO_H2_10, 19000);
                break;
            case EVENT_INTRO_H2_10:
                DoScriptText(SAY_SYLVANAS_INTRO_6, me);
                events.ScheduleEvent(EVENT_INTRO_H2_11, 1500);
                break;
            case EVENT_INTRO_H2_11:
                if (Creature* pUther = me->GetCreature(*me, uiUther))
                    DoScriptText(SAY_UTHER_INTRO_H2_4, pUther);
                events.ScheduleEvent(EVENT_INTRO_H2_12, 19500);
                break;
            case EVENT_INTRO_H2_12:
                DoScriptText(SAY_SYLVANAS_INTRO_7, me);
                events.ScheduleEvent(EVENT_INTRO_H2_13, 2000);
                break;
            case EVENT_INTRO_H2_13:
                if (Creature* pUther = me->GetCreature(*me, uiUther))
                {
                    pUther->HandleEmoteCommand(EMOTE_ONESHOT_NO);
                    DoScriptText(SAY_UTHER_INTRO_H2_5, pUther);
                }
                events.ScheduleEvent(EVENT_INTRO_H2_14, 12000);
                break;
            case EVENT_INTRO_H2_14:
                if (Creature* pUther = me->GetCreature(*me, uiUther))
                    DoScriptText(SAY_UTHER_INTRO_H2_6, pUther);
                events.ScheduleEvent(EVENT_INTRO_H2_15, 8000);
                break;
            case EVENT_INTRO_H2_15:
                DoScriptText(SAY_SYLVANAS_INTRO_8, me);
                events.ScheduleEvent(EVENT_INTRO_LK_1, 2000);
                break;
                
        // Remaining Intro Events common for both faction
            case EVENT_INTRO_LK_1:
                // Spawn LK in front of door, and make him move to the sword.
                if (Creature* pLichKing = me->SummonCreature(NPC_LICH_KING_EVENT, LichKingSpawnPos, TEMPSUMMON_MANUAL_DESPAWN))
                {
                    pLichKing->GetMotionMaster()->MovePoint(0, LichKingMoveThronePos);
                    pLichKing->SetReactState(REACT_PASSIVE);
                    uiLichKing = pLichKing->GetGUID();
                }

                if (Creature* pUther = me->GetCreature(*me, uiUther))
                    if (pInstance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE)
                        DoScriptText(SAY_UTHER_INTRO_A2_9, pUther);
                    else
                        DoScriptText(SAY_UTHER_INTRO_H2_7, pUther);

                events.ScheduleEvent(EVENT_INTRO_LK_2, 11000);
                break;

            case EVENT_INTRO_LK_2:
                 if (Creature* pLichKing = me->GetCreature(*me, uiLichKing))
                     DoScriptText(SAY_LK_INTRO_1, pLichKing);
                 events.ScheduleEvent(EVENT_INTRO_LK_3, 2000);
                 break;

            case EVENT_INTRO_LK_3:
                 // The Lich King banishes Uther to the abyss. 
                 if (Creature* pUther = me->GetCreature(*me, uiUther))
                 {
                     pUther->DisappearAndDie();
                     uiUther = 0;
                 }
                 
                 // He steps forward and removes the runeblade from the heap of skulls. 
                 
                 events.ScheduleEvent(EVENT_INTRO_LK_4, 4000);
                 break;
                 
            case EVENT_INTRO_LK_4:
                  if (Creature* pLichKing = me->GetCreature(*me, uiLichKing))
                      DoScriptText(SAY_LK_INTRO_2, pLichKing);
                events.ScheduleEvent(EVENT_INTRO_LK_5, 10000);
                break;
                       
            case EVENT_INTRO_LK_5:
                // summon Falric and Marwyn. then go back to the door
                if (Creature* pFalric = me->GetCreature(*me, pInstance->GetData64(DATA_FALRIC)))
                    pFalric->SetVisibility(VISIBILITY_ON);
                if (Creature* pMarwyn = me->GetCreature(*me, pInstance->GetData64(DATA_MARWYN)))
                    pMarwyn->SetVisibility(VISIBILITY_ON);
                
                if (Creature* pLichKing = me->GetCreature(*me, uiLichKing))
                {
                    pLichKing->GetMotionMaster()->MovePoint(0, LichKingSpawnPos);
                    DoScriptText(SAY_LK_INTRO_3, pLichKing);
                }
                
                events.ScheduleEvent(EVENT_INTRO_LK_6, 8000);
                break;
                
            case EVENT_INTRO_LK_6:
                if (Creature* pFalric = me->GetCreature(*me, pInstance->GetData64(DATA_FALRIC)))
                    DoScriptText(SAY_FALRIC_INTRO_1, pFalric);

                events.ScheduleEvent(EVENT_INTRO_LK_7, 2000);
                break;

            case EVENT_INTRO_LK_7:
                if (Creature* pMarwyn = me->GetCreature(*me, pInstance->GetData64(DATA_MARWYN)))
                    DoScriptText(SAY_MARWYN_INTRO_1, pMarwyn);

                events.ScheduleEvent(EVENT_INTRO_LK_8, 2000);
                break;
                
            case EVENT_INTRO_LK_8:
                if (Creature* pFalric = me->GetCreature(*me, pInstance->GetData64(DATA_FALRIC)))
                    DoScriptText(SAY_FALRIC_INTRO_2, pFalric);

                events.ScheduleEvent(EVENT_INTRO_LK_9, 5000);
                break;

            case EVENT_INTRO_LK_9:
                if (pInstance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE)
                    DoScriptText(SAY_JAINA_INTRO_END, me);
                else
                    DoScriptText(SAY_SYLVANAS_INTRO_END, me);

                me->GetMotionMaster()->MovePoint(0, LichKingSpawnPos);
                // TODO: Loralen/Koreln shall run also
                events.ScheduleEvent(EVENT_INTRO_END, 10000);
                break;
            
            case EVENT_INTRO_END:
                if (pInstance)
                    pInstance->SetData(DATA_WAVE_COUNT, SPECIAL);   // start first wave

                // Loralen or Koreln disappearAndDie()
                me->DisappearAndDie();
                break;

            case EVENT_SKIP_INTRO:
                // TODO: implement

                if (Creature* pFalric = me->GetCreature(*me, pInstance->GetData64(DATA_FALRIC)))
                    pFalric->SetVisibility(VISIBILITY_ON);
                if (Creature* pMarwyn = me->GetCreature(*me, pInstance->GetData64(DATA_MARWYN)))
                    pMarwyn->SetVisibility(VISIBILITY_ON);

                me->GetMotionMaster()->MovePoint(0, LichKingSpawnPos);
                // TODO: Loralen/Koreln shall run also

                events.ScheduleEvent(EVENT_INTRO_END, 15000);
                break;
        }
    }
};

bool GossipHello_npc_sylvanas_hor(Player* pPlayer, Creature* pCreature)
{
    if (pCreature->isQuestGiver())
        pPlayer->PrepareQuestMenu(pCreature->GetGUID());

    if (pPlayer->GetQuestStatus(QUEST_DELIVRANCE_FROM_THE_PIT_H2) == QUEST_STATUS_COMPLETE)
        pPlayer->ADD_GOSSIP_ITEM( 0, "Can you remove the sword?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
    
    // once last quest is completed, she offers this shortcut of the starting event
    if (pPlayer->GetQuestStatus(QUEST_WRATH_OF_THE_LICH_KING_H2) == QUEST_STATUS_COMPLETE)
        pPlayer->ADD_GOSSIP_ITEM( 0, "Dark Lady, I think I hear Arthas coming. Whatever you're going to do, do it quickly.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);

    pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, pCreature->GetGUID());

    return true;
}

bool GossipHello_npc_jaina_hor(Player* pPlayer, Creature* pCreature)
{
    if (pCreature->isQuestGiver())
        pPlayer->PrepareQuestMenu(pCreature->GetGUID());

    if (pPlayer->GetQuestStatus(QUEST_DELIVRANCE_FROM_THE_PIT_A2) == QUEST_STATUS_COMPLETE)
        pPlayer->ADD_GOSSIP_ITEM( 0, "Can you remove the sword?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
    
    // once last quest of the series is completed, she offers this shortcut of the starting event
    if (pPlayer->GetQuestStatus(QUEST_WRATH_OF_THE_LICH_KING_A2) == QUEST_STATUS_COMPLETE)
        pPlayer->ADD_GOSSIP_ITEM( 0, "My Lady, I think I hear Arthas coming. Whatever you're going to do, do it quickly.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);

    pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, pCreature->GetGUID());
    return true;
}

bool GossipSelect_npc_jaina_or_sylvanas_hor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
    switch (uiAction)
    {
        case GOSSIP_ACTION_INFO_DEF+1:
            pPlayer->CLOSE_GOSSIP_MENU();
            if (pCreature->AI())
                pCreature->AI()->DoAction(ACTION_START_INTRO);
            pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
            break;
        case GOSSIP_ACTION_INFO_DEF+2:
            pPlayer->CLOSE_GOSSIP_MENU();
            if (pCreature->AI())
                pCreature->AI()->DoAction(ACTION_SKIP_INTRO);
            pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
            break;
    }

    return true;
}

enum TrashSpells
{
    // Ghostly Priest
    SPELL_SHADOW_WORD_PAIN                        = 72318,
    SPELL_CIRCLE_OF_DESTRUCTION                   = 72320,
    SPELL_COWER_IN_FEAR                           = 72321,
    SPELL_DARK_MENDING                            = 72322,

    // Phantom Mage
    SPELL_FIREBALL                                = 72163,
    SPELL_FLAMESTRIKE                             = 72169,
    SPELL_FROSTBOLT                               = 72166,
    SPELL_CHAINS_OF_ICE                           = 72121,
    SPELL_HALLUCINATION                           = 72342,

    // Phantom Hallucination (same as phantom mage + HALLUCINATION_2 when dies)
    SPELL_HALLUCINATION_2                         = 72344,

    // Shadowy Mercenary
    SPELL_SHADOW_STEP                             = 72326,
    SPELL_DEADLY_POISON                           = 72329,
    SPELL_ENVENOMED_DAGGER_THROW                  = 72333,
    SPELL_KIDNEY_SHOT                             = 72335,

    // Spectral Footman
    SPELL_SPECTRAL_STRIKE                         = 72198,
    SPELL_SHIELD_BASH                             = 72194,
    SPELL_TORTURED_ENRAGE                         = 72203,

    // Tortured Rifleman
    SPELL_SHOOT                                   = 72208,
    SPELL_CURSED_ARROW                            = 72222,
    SPELL_FROST_TRAP                              = 72215,
    SPELL_ICE_SHOT                                = 72268,
};

enum TrashEvents
{
    EVENT_TRASH_NONE,

    // Ghostly Priest
    EVENT_SHADOW_WORD_PAIN,
    EVENT_CIRCLE_OF_DESTRUCTION,
    EVENT_COWER_IN_FEAR,
    EVENT_DARK_MENDING,

    // Phantom Mage
    EVENT_FIREBALL,
    EVENT_FLAMESTRIKE,
    EVENT_FROSTBOLT,
    EVENT_CHAINS_OF_ICE,
    EVENT_HALLUCINATION,

    // Shadowy Mercenary
    EVENT_SHADOW_STEP,
    EVENT_DEADLY_POISON,
    EVENT_ENVENOMED_DAGGER_THROW,
    EVENT_KIDNEY_SHOT,

    // Spectral Footman
    EVENT_SPECTRAL_STRIKE,
    EVENT_SHIELD_BASH,
    EVENT_TORTURED_ENRAGE,

    // Tortured Rifleman
    EVENT_SHOOT,
    EVENT_CURSED_ARROW,
    EVENT_FROST_TRAP,
    EVENT_ICE_SHOT,
};

struct npc_ghostly_priestAI: public ScriptedAI
{
    npc_ghostly_priestAI(Creature *c) : ScriptedAI(c)
    {
    }

    EventMap events;

    void Reset()
    {
        events.Reset();
    }

    void EnterCombat(Unit* /*who*/)
    {
        events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 8000); // TODO: adjust timers
        events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12000);
        events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10000);
        events.ScheduleEvent(EVENT_DARK_MENDING, 20000);
    }

    void UpdateAI(const uint32 diff)
    {
        if (!UpdateVictim())
            return;

        events.Update(diff);

        if (me->hasUnitState(UNIT_STAT_CASTING))
            return;

        while (uint32 eventId = events.ExecuteEvent())
        {
            switch(eventId)
            {
                case EVENT_SHADOW_WORD_PAIN:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_SHADOW_WORD_PAIN);
                    events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 8000);
                    return;
                case EVENT_CIRCLE_OF_DESTRUCTION:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_CIRCLE_OF_DESTRUCTION);
                    events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12000);
                    return;
                case EVENT_COWER_IN_FEAR:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_COWER_IN_FEAR);
                    events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10000);
                    return;
                case EVENT_DARK_MENDING:
                    // find an ally with missing HP
                    if (Unit *pTarget = DoSelectLowestHpFriendly(40, DUNGEON_MODE(30000,50000)))
                    {
                        DoCast(pTarget, SPELL_DARK_MENDING);
                        events.ScheduleEvent(EVENT_DARK_MENDING, 20000);
                    }
                    else
                    {
                        // no friendly unit with missing hp. re-check in just 5 sec.
                        events.ScheduleEvent(EVENT_DARK_MENDING, 5000);
                    }
                    return;
            }
        }

        DoMeleeAttackIfReady();
    }
};

struct npc_phantom_mageAI: public ScriptedAI
{
    npc_phantom_mageAI(Creature *c) : ScriptedAI(c)
    {
    }

    EventMap events;

    void Reset()
    {
        events.Reset();
    }

    void EnterCombat(Unit* /*who*/)
    {
        events.ScheduleEvent(EVENT_FIREBALL, 3000); // TODO: adjust timers
        events.ScheduleEvent(EVENT_FLAMESTRIKE, 6000);
        events.ScheduleEvent(EVENT_FROSTBOLT, 9000);
        events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12000);
        events.ScheduleEvent(EVENT_HALLUCINATION, 40000);
    }

    void UpdateAI(const uint32 diff)
    {
        if (!UpdateVictim())
            return;

        events.Update(diff);

        if (me->hasUnitState(UNIT_STAT_CASTING))
            return;

        while (uint32 eventId = events.ExecuteEvent())
        {
            switch(eventId)
            {
                case EVENT_FIREBALL:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_FIREBALL);
                    events.ScheduleEvent(EVENT_FIREBALL, 15000);
                    return;
                case EVENT_FLAMESTRIKE:
                    DoCast(SPELL_FLAMESTRIKE);
                    events.ScheduleEvent(EVENT_FLAMESTRIKE, 15000);
                    return;
                case EVENT_FROSTBOLT:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_FROSTBOLT);
                    events.ScheduleEvent(EVENT_FROSTBOLT, 15000);
                    return;
                case EVENT_CHAINS_OF_ICE:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_CHAINS_OF_ICE);
                    events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 15000);
                    return;
                case EVENT_HALLUCINATION:
                    DoCast(SPELL_HALLUCINATION);
                    return;
            }
        }

        DoMeleeAttackIfReady();
    }
};

struct npc_phantom_hallucinationAI: public npc_phantom_mageAI
{
    npc_phantom_hallucinationAI(Creature *c) : npc_phantom_mageAI(c)
    {
    }

    void JustDied(Unit * /*pWho*/)
    {
        DoCast(SPELL_HALLUCINATION_2);
    }
};

struct npc_shadowy_mercenaryAI: public ScriptedAI
{
    npc_shadowy_mercenaryAI(Creature *c) : ScriptedAI(c)
    {
    }

    EventMap events;

    void Reset()
    {
        events.Reset();
    }

    void EnterCombat(Unit* /*who*/)
    {
        events.ScheduleEvent(EVENT_SHADOW_STEP, 8000); // TODO: adjust timers
        events.ScheduleEvent(EVENT_DEADLY_POISON, 5000);
        events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10000);
        events.ScheduleEvent(EVENT_KIDNEY_SHOT, 12000);
    }

    void UpdateAI(const uint32 diff)
    {
        if (!UpdateVictim())
            return;

        events.Update(diff);

        if (me->hasUnitState(UNIT_STAT_CASTING))
            return;

        while (uint32 eventId = events.ExecuteEvent())
        {
            switch(eventId)
            {
                case EVENT_SHADOW_STEP:
                    DoCast(SPELL_SHADOW_STEP);
                    events.ScheduleEvent(EVENT_SHADOW_STEP, 8000);
                    return;
                case EVENT_DEADLY_POISON:
                    DoCast(me->getVictim(), SPELL_DEADLY_POISON);
                    events.ScheduleEvent(EVENT_DEADLY_POISON, 10000);
                    return;
                case EVENT_ENVENOMED_DAGGER_THROW:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_ENVENOMED_DAGGER_THROW);
                    events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10000);
                    return;
                case EVENT_KIDNEY_SHOT:
                    DoCast(me->getVictim(), SPELL_KIDNEY_SHOT);
                    events.ScheduleEvent(EVENT_KIDNEY_SHOT, 10000);
                    return;
            }
        }

        DoMeleeAttackIfReady();
    }
};

struct npc_spectral_footmanAI: public ScriptedAI
{
    npc_spectral_footmanAI(Creature *c) : ScriptedAI(c)
    {
    }

    EventMap events;

    void Reset()
    {
        events.Reset();
    }

    void EnterCombat(Unit* /*who*/)
    {
        events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5000); // TODO: adjust timers
        events.ScheduleEvent(EVENT_SHIELD_BASH, 10000);
        events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15000);
    }

    void UpdateAI(const uint32 diff)
    {
        if (!UpdateVictim())
            return;

        events.Update(diff);

        if (me->hasUnitState(UNIT_STAT_CASTING))
            return;

        while (uint32 eventId = events.ExecuteEvent())
        {
            switch(eventId)
            {
                case EVENT_SPECTRAL_STRIKE:
                    DoCast(me->getVictim(), SPELL_SPECTRAL_STRIKE);
                    events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5000);
                    return;
                case EVENT_SHIELD_BASH:
                    DoCast(me->getVictim(), SPELL_SHIELD_BASH);
                    events.ScheduleEvent(EVENT_SHIELD_BASH, 5000);
                    return;
                case EVENT_TORTURED_ENRAGE:
                    DoCast(SPELL_TORTURED_ENRAGE);
                    events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15000);
                    return;
            }
        }

        DoMeleeAttackIfReady();
    }
};

struct npc_tortured_riflemanAI  : public ScriptedAI
{
    npc_tortured_riflemanAI(Creature *c) : ScriptedAI(c)
    {
    }

    EventMap events;

    void Reset()
    {
        events.Reset();
    }

    void EnterCombat(Unit* /*who*/)
    {
        events.ScheduleEvent(EVENT_SHOOT, 2000); // TODO: adjust timers
        events.ScheduleEvent(EVENT_CURSED_ARROW, 10000);
        events.ScheduleEvent(EVENT_FROST_TRAP, 1000);
        events.ScheduleEvent(EVENT_ICE_SHOT, 15000);
    }

    void UpdateAI(const uint32 diff)
    {
        if (!UpdateVictim())
            return;

        events.Update(diff);

        if (me->hasUnitState(UNIT_STAT_CASTING))
            return;

        while (uint32 eventId = events.ExecuteEvent())
        {
            switch(eventId)
            {
                case EVENT_SHOOT:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_SHOOT);
                    events.ScheduleEvent(EVENT_SHOOT, 2000);
                    return;
                case EVENT_CURSED_ARROW:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_CURSED_ARROW);
                    events.ScheduleEvent(EVENT_CURSED_ARROW, 10000);
                    return;
                case EVENT_FROST_TRAP:
                    DoCast(SPELL_FROST_TRAP);
                    events.ScheduleEvent(EVENT_FROST_TRAP, 30000);
                    return;
                case EVENT_ICE_SHOT:
                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                        DoCast(pTarget, SPELL_ICE_SHOT);
                    events.ScheduleEvent(EVENT_ICE_SHOT, 15000);
                    return;
            }
        }

        DoMeleeAttackIfReady();
    }
};

CreatureAI* GetAI_npc_jaina_or_sylvanas_horAI(Creature* pCreature)
{
    return new npc_jaina_or_sylvanas_horAI(pCreature);
}

CreatureAI* GetAI_npc_ghostly_priestAI(Creature* pCreature)
{
    return new npc_ghostly_priestAI(pCreature);
}

CreatureAI* GetAI_npc_phantom_mageAI(Creature* pCreature)
{
    return new npc_phantom_mageAI(pCreature);
}

CreatureAI* GetAI_npc_phantom_hallucinationAI(Creature* pCreature)
{
    return new npc_phantom_hallucinationAI(pCreature);
}

CreatureAI* GetAI_npc_shadowy_mercenaryAI(Creature* pCreature)
{
    return new npc_shadowy_mercenaryAI(pCreature);
}

CreatureAI* GetAI_npc_spectral_footmanAI(Creature* pCreature)
{
    return new npc_spectral_footmanAI(pCreature);
}

CreatureAI* GetAI_npc_tortured_riflemanAI(Creature* pCreature)
{
    return new npc_tortured_riflemanAI(pCreature);
}

void AddSC_halls_of_reflection()
{
    Script *newscript;

    newscript = new Script;
    newscript->Name = "npc_sylvanas_hor_part1";
    newscript->GetAI = &GetAI_npc_jaina_or_sylvanas_horAI;
    newscript->pGossipHello =  &GossipHello_npc_sylvanas_hor;
    newscript->pGossipSelect = &GossipSelect_npc_jaina_or_sylvanas_hor;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_jaina_hor_part1";
    newscript->GetAI = &GetAI_npc_jaina_or_sylvanas_horAI;
    newscript->pGossipHello =  &GossipHello_npc_jaina_hor;
    newscript->pGossipSelect = &GossipSelect_npc_jaina_or_sylvanas_hor;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_ghostly_priest";
    newscript->GetAI = &GetAI_npc_ghostly_priestAI;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_phantom_mage";
    newscript->GetAI = &GetAI_npc_phantom_mageAI;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_phantom_hallucination";
    newscript->GetAI = &GetAI_npc_phantom_hallucinationAI;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_shadowy_mercenary";
    newscript->GetAI = &GetAI_npc_shadowy_mercenaryAI;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_spectral_footman";
    newscript->GetAI = &GetAI_npc_spectral_footmanAI;
    newscript->RegisterSelf();

    newscript = new Script;
    newscript->Name = "npc_tortured_rifleman";
    newscript->GetAI = &GetAI_npc_tortured_riflemanAI;
    newscript->RegisterSelf();
}
